package cn.autumnorange.app.common.repository.impl;

import cn.autumnorange.app.common.repository.AbstractPersisTableRepository;
import cn.autumnorange.app.common.repository.util.EntityUtil;
import org.hibernate.query.NativeQuery;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.transform.Transformers;
import org.hibernate.type.Type;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import java.io.Serializable;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.*;

@Transactional(rollbackFor = Exception.class, readOnly = false, propagation = Propagation.REQUIRED)
public abstract class AbstractPersisTableRepositoryImpl<I extends Serializable>
        implements AbstractPersisTableRepository<I>
//        , ProxyEntityManager
{
    //  private final String primaryKeyName1 = "primaryKeyName1";
    //  private final String primaryKeyName2 = "primaryKeyName2";

    @PersistenceContext
    protected EntityManager entityManager;

    @Override
    public EntityManager getEntityManager() {
        return entityManager;
    }

    protected void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    @Override
    public <T> T save(T entity) {
        entityManager.persist(entity);
        return entity;
    }

    @Override
    public <T> T update(T entity) {
        return entityManager.merge(entity);
    }

    @Override
    public <T> T findById(Class<T> clazz, I id) {
        return entityManager.find(clazz, id);
    }

    @Override
    public <T> List<T> findByFiled(Class<T> clazz, String filed, Object o) {
        String hql = "from " + EntityUtil.getHqlTableName(clazz) + " u WHERE u." + filed + "=:" + filed;
        Query query = entityManager.createQuery(hql);
        query.setParameter(filed, o);
        return query.getResultList();
    }

    @Override
    public <T> List<T> findByFileds(Class<T> clazz, LinkedHashMap<String, Object> linkedHashMap) {

        String sql = "from " + EntityUtil.getHqlTableName(clazz) + " u WHERE ";
        Set<String> linkedKeySet = linkedHashMap.keySet();
        Collection collectionValues = linkedHashMap.values();

        for (String filed : linkedKeySet) {
            sql += "u." + filed + "=? and ";
        }
        sql = sql.substring(0, sql.length() - 4);
        Query query = entityManager.createQuery(sql);

        Iterator<Object> iterator = collectionValues.iterator();
        int i = 0;
        while (iterator.hasNext()) {
            query.setParameter(i, iterator.next());
            i++;
        }

        return query.getResultList();
    }

    @Override
    public <T> List<T> findAll(Class<T> clazz) {
        String hql = " from " + EntityUtil.getHqlTableName(clazz) + " u ";
        Query query = entityManager.createQuery(hql);
        return query.getResultList();
    }

    @Override
    public <T> List<T> findByNativeQuery(
            String sql, Map<String, Object> conditionParams, ResultTransformer resultTransformer, Map<String, Type> scalar) {

        Query nativeQuery = entityManager.createNativeQuery(sql);
        if (conditionParams != null) {
            for (String paramKey : conditionParams.keySet()) {
                nativeQuery.setParameter(paramKey, conditionParams.get(paramKey));
            }
        }
        if (resultTransformer != null) {
            NativeQuery nativeQueryImpl = nativeQuery.unwrap(NativeQuery.class);
            nativeQueryImpl.setResultTransformer(resultTransformer);
//          Persistable.ID, new LongType()
            if (scalar != null) {
                for (Map.Entry<String, Type> entry : scalar.entrySet()) {
                    nativeQueryImpl.addScalar(entry.getKey(), entry.getValue());
                }
            }
        }
        List<T> list = nativeQuery.getResultList();
        return list;
    }

    @Override
    public <T> List<T> findPageByQueryFiled(
            Class<T> clazz, String filed, Object o, int pageNo, int pageSize) {
        String sql = "from " + EntityUtil.getHqlTableName(clazz) + " u WHERE u." + filed + "=?";
        List<T> list = new ArrayList<>();
        //    try {
        Query query = entityManager.createQuery(sql);
        query.setParameter(1, o);
        query.setFirstResult((pageNo - 1) * pageSize);
        query.setMaxResults(pageSize);
        list = query.getResultList();
        //    } catch (Exception e) {
        //      e.printStackTrace();
        //    } finally {
        //      entityManager.close();
        //    }
        return list;
    }

    @Override
    public Map<String, Object> findPageByNativeQueryFileds(
            String sql,
            Map<String, Object> conditionParams,
            ResultTransformer resultTransformer,
            Integer pageNo,
            Integer pageSize) {

        String countSql = "select count(1) from (" + sql + ") u";
        Query nativeQuery = entityManager.createNativeQuery(sql);
        Query nativeCountQuery = entityManager.createNativeQuery(countSql);
        if (conditionParams != null) {
            for (String paramKey : conditionParams.keySet()) {
                nativeCountQuery.setParameter(paramKey, conditionParams.get(paramKey));
                nativeQuery.setParameter(paramKey, conditionParams.get(paramKey));
            }
        }
        List totalList = nativeCountQuery.getResultList();
        BigInteger total = (BigInteger) totalList.get(0);
        if (pageNo != null) {
            nativeQuery.setFirstResult((pageNo - 1) * pageSize);
        }
        if (pageSize != null) {
            nativeQuery.setMaxResults(pageSize);
        }
        if (resultTransformer != null) {
            nativeQuery.unwrap(NativeQuery.class).setResultTransformer(resultTransformer);
        }
        List rows = nativeQuery.getResultList();
        Map<String, Object> resultMap = new HashMap<>(2);
        resultMap.put(ROWS, rows);
        resultMap.put(TOTAL, total);
        return resultMap;
    }

    @Override
    public <T> int updateFileds(
            Class<T> clazz,
            String clazzIsNullHqlOrClazzIsNotNullSetAndWhereCondition,
            Map<String, Object> conditionParams,
            Map<String, Object> valueParams) {
        //    ameter(key, map.get(key));
        //    }
        //    query.setParameter(idKey, idValue);    StringBuffer sql = new StringBuffer();
        ////    sql.append("UPDATE ").append(EntityUtil.getHqlTableName(clazz)).append(" AS u SET ");
        ////
        ////    Set<String> set = map.keySet();
        ////    String idKey = "id";
        ////    String idValue = map.get(idKey).toString();
        ////    map.remove(idKey);
        ////    Iterator<String> iterator = set.iterator();
        ////    while (iterator.hasNext()) {
        ////      String key = iterator.next();
        ////      sql.append("u.").append(key).append("=:").append(key);
        ////      if (iterator.hasNext()) {
        ////        sql.append(",");
        ////      }
        ////    }
        ////    sql.append(" WHERE u.id=:id");
        ////    Query query = entityManager.createQuery(sql.toString());
        ////    for (String key : map.keySet()) {
        ////      query.setParameter(key, map.get(key));
        ////    }
        ////    query.setParameter(idKey, idValue);
        //    int resurlt = query.executeUpdate();
        if (clazz != null) {
            StringBuffer clazzNotNullSql = new StringBuffer();
            clazzNotNullSql
                    .append("UPDATE ")
                    .append(EntityUtil.getHqlTableName(clazz))
                    .append(" ")
                    .append(clazzIsNullHqlOrClazzIsNotNullSetAndWhereCondition);
            clazzIsNullHqlOrClazzIsNotNullSetAndWhereCondition = clazzNotNullSql.toString();
        }
        Query query = entityManager.createQuery(clazzIsNullHqlOrClazzIsNotNullSetAndWhereCondition);
        if (conditionParams != null) {
            for (String paramKey : conditionParams.keySet()) {
                query.setParameter(paramKey, conditionParams.get(paramKey));
            }
        }
        if (valueParams != null) {
            for (String paramKey : valueParams.keySet()) {
                query.setParameter(paramKey, valueParams.get(paramKey));
            }
        }
        return query.executeUpdate();
    }

    @Override
    public <T> boolean remove(T entity) {
        boolean flag = false;
        entityManager.remove(entityManager.merge(entity));
        flag = true;
        return flag;
    }

    @Override
    public <T> boolean deleteById(Class<T> clazz, I id) {
        String hql = "delete from " + EntityUtil.getHqlTableName(clazz) + " u WHERE u.id = " + id + "";
        Query query = entityManager.createQuery(hql);
        if (query.executeUpdate() > 0) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public <T> boolean deleteByMap(Class<T> clazz, Map paramsMap) {
        String hql = "delete from " + EntityUtil.getHqlTableName(clazz) + " u WHERE ";
        if (paramsMap == null || paramsMap.isEmpty()) {
            return false;
        }
        for (Object key : paramsMap.keySet()) {
            if ("java.lang.String".equals(paramsMap.get(key).getClass().getTypeName())) {
                hql += " u." + key + "='" + paramsMap.get(key) + "' AND ";
            } else {
                hql += " u." + key + "=" + paramsMap.get(key) + " AND ";
            }
        }
        hql = hql.substring(0, hql.length() - 4);
        Query query = entityManager.createQuery(hql);
        if (query.executeUpdate() > 0) {
            return true;
        } else {
            return false;
        }
    }

    //  Query nativeQuery = entityManager.createNativeQuery(sql);
    //    if (conditionParams != null) {
    //    for (String paramKey : conditionParams.keySet()) {
    //      nativeQuery.setParameter(paramKey, conditionParams.get(paramKey));
    //    }
    //  }
    //    if (resultTransformer != null) {
    //    nativeQuery.unwrap(NativeQuery.class).setResultTransformer(resultTransformer);
    //  }
    //  List<T> list = nativeQuery.getResultList();
    //    return list;
    @Override
    public <T> boolean deleteByMap(Class<T> clazz, String hql, Map<String, Object> paramsMap) {
        Query query = entityManager.createQuery(hql);
        for (Map.Entry<String, Object> entry : paramsMap.entrySet()) {
            query.setParameter(entry.getKey(), entry.getValue());
        }
        if (query.executeUpdate() > 0) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public <T> T findByIdClass(T entity, Object id) {
        return (T) entityManager.find(entity.getClass(), id);
    }

    @Override
    public <MT> boolean addMiddleTable(
            Class<MT> clazz,
            String primaryKeyName1,
            String primaryKeyValue1,
            String primaryKeyName2,
            String primaryKeyValue2,
            Timestamp createTime, Map<String, Type> scalar) {
        String sql =
                "INSERT INTO "
                        + EntityUtil.getSqlTableName(clazz)
                        + " (`roleId`, `userId`, `createTime`) VALUES (:roleId,:userId,:createTime);";
        Map paramMap = new HashMap();
        paramMap.put(primaryKeyName1, primaryKeyValue1);
        paramMap.put(primaryKeyName2, primaryKeyValue2);
        List<MT> resList = findByNativeQuery(sql, paramMap, Transformers.aliasToBean(clazz), scalar);

        return false;
    }

    @Override
    public <MT> MT findMiddleTable(
            Class<MT> clazz,
            String primaryKeyName1,
            String primaryKeyValue1,
            String primaryKeyName2,
            String primaryKeyValue2, Map<String, Type> scalar) {
        String sql =
                "select * from "
                        + EntityUtil.getSqlTableName(clazz)
                        + " where "
                        + primaryKeyName1
                        + "=:"
                        + primaryKeyName1
                        + " and "
                        + primaryKeyName2
                        + "=:"
                        + primaryKeyName2;
        Map paramMap = new HashMap();
        paramMap.put(primaryKeyName1, primaryKeyValue1);
        paramMap.put(primaryKeyName2, primaryKeyValue2);
        //    List<MT> resList = entityManager.createQuery(sql, paramMap,
        // Transformers.aliasToBean(clazz));
        //    if (resList.size() != 0) {
        //      return resList.get(0);
        //    }
        List<MT> list = this.findByNativeQuery(sql, paramMap, Transformers.aliasToBean(clazz), scalar);
        if (list.size() > 1) {
            throw new RuntimeException("queryMiddle error");
        }
        if (list.size() == 0) {
            return null;
        }
        return (MT) list.get(0);
    }

    @Override
    public <MT> boolean delMiddleTable(
            Class<MT> clazz,
            String primaryKeyName1,
            String primaryKeyValue1,
            String primaryKeyName2,
            String primaryKeyValue2) {
        String sql =
                "DELETE FROM "
                        + EntityUtil.getSqlTableName(clazz)
                        + " where "
                        + primaryKeyName1
                        + "=:"
                        + primaryKeyName1;
        Map paramMap = new HashMap();
        paramMap.put(primaryKeyName1, primaryKeyValue1);
        if (primaryKeyName2 != null && primaryKeyValue2 != null) {
            sql += " and " + primaryKeyName2 + "=:" + primaryKeyName2;
            paramMap.put(primaryKeyName2, primaryKeyValue2);
        }
        return this.deleteByMap(clazz, paramMap);
    }

    @Override
    public <MT> boolean updMiddleTable(
            Class<MT> clazz, String name1, String value1, String name2, String value2) {
        return false;
    }

    //    @Override
    //    public <I> List<I> resultList(Class<I> iClass, String ql, Iterable<Object> parameters) {
    //        TypedQuery<I> query = entityManager.createQuery(ql, iClass);
    //        query(query, parameters);
    //        return query.getResultList();
    //    }
    //
    //    @Override
    //    @SuppressWarnings("unchecked")
    //    public List<Object> resultList(String ql, Iterable<Object> parameters) {
    //        Query query = entityManager.createQuery(ql);
    //        query(query, parameters);
    //        return query.getResultList();
    //    }

    protected void query(Query query, Iterable<Object> parameters) {
        int i = 1;
        for (Object par : parameters) {
            query.setParameter(i++, par);
        }
    }

    protected <T> TypedQuery<T> query(Class<T> clazz, String hql) {
        return entityManager.createQuery(hql, clazz);
    }

    protected <T> TypedQuery<T> query(Class<T> clazz, String hql, Object arg) {
        TypedQuery<T> tqSelect = entityManager.createQuery(hql, clazz);
        return tqSelect.setParameter(1, arg);
    }

    protected <T> TypedQuery<T> query(Class<T> clazz, String hql, Object arg, Object arg2) {
        TypedQuery<T> tqSelect = entityManager.createQuery(hql, clazz);
        return tqSelect.setParameter(1, arg).setParameter(2, arg2);
    }

    protected <T> TypedQuery<T> query(Class<T> clazz, String hql, Object... args) {
        TypedQuery<T> tqSelect = entityManager.createQuery(hql, clazz);
        for (int i = 0, l = args.length; i < l; i++) {
            tqSelect.setParameter(i + 1, args[i]);
        }
        return tqSelect;
    }

    // ---------------------------------exsit
    protected boolean exsit(String ql) {
        return exsit(query(Number.class, ql));
    }

    protected boolean exsit(String ql, Object arg) {
        return exsit(query(Number.class, ql, arg));
    }

    protected boolean exsit(String ql, Object arg, Object arg2) {
        return exsit(query(Number.class, ql, arg, arg2));
    }

    protected boolean exsit(String ql, Object... args) {
        return exsit(query(Number.class, ql, args));
    }

    protected boolean exsit(TypedQuery<Number> t) {
        return t.getSingleResult().intValue() > 0;
    }

    // ---------------------------------first
    @Override
    public <T> T first(TypedQuery<T> tqSelect) {
        List<T> items = tqSelect.setMaxResults(1).getResultList();
        return items.isEmpty() ? null : items.get(0);
    }

    @Override
    public <T> T first(Class<T> clazz, String hql) {
        return first(query(clazz, hql));
    }

    @Override
    public <T> T first(Class<T> clazz, String hql, Object arg) {
        return first(query(clazz, hql, arg));
    }

    @Override
    public <T> T first(Class<T> clazz, String hql, Object arg, Object arg2) {
        return first(query(clazz, hql, arg, arg2));
    }

    @Override
    public <T> T first(Class<T> clazz, String hql, Object... args) {
        return first(query(clazz, hql, args));
    }

    // ---------------------------------single
    @Override
    public <T> T single(Class<T> clazz, String hql) {
        return query(clazz, hql).getSingleResult();
    }

    @Override
    public <T> T single(Class<T> clazz, String hql, Object arg) {
        return query(clazz, hql, arg).getSingleResult();
    }

    @Override
    public <T> T single(Class<T> clazz, String hql, Object arg, Object arg2) {
        return query(clazz, hql, arg, arg2).getSingleResult();
    }

    @Override
    public <T> T single(Class<T> clazz, String hql, Object... args) {
        return query(clazz, hql, args).getSingleResult();
    }

    // ---------------------------------list
    @Override
    public <T> List<T> list(Class<T> clazz, String hql) {
        return query(clazz, hql).getResultList();
    }

    @Override
    public <T> List<T> list(Class<T> clazz, String hql, Object arg) {
        return query(clazz, hql, arg).getResultList();
    }

    @Override
    public <T> List<T> list(Class<T> clazz, String hql, Object arg, Object arg2) {
        return query(clazz, hql, arg, arg2).getResultList();
    }

    @Override
    public <T> List<T> list(Class<T> clazz, String hql, Object... args) {
        return query(clazz, hql, args).getResultList();
    }

    // ---------------------------------top
    @Override
    public <T> List<T> top(int top, Class<T> clazz, String hql) {
        return query(clazz, hql).setMaxResults(top).getResultList();
    }

    @Override
    public <T> List<T> top(int top, Class<T> clazz, String hql, Object arg) {
        return query(clazz, hql, arg).setMaxResults(top).getResultList();
    }

    @Override
    public <T> List<T> top(int top, Class<T> clazz, String hql, Object arg, Object arg2) {
        return query(clazz, hql, arg, arg2).setMaxResults(top).getResultList();
    }

    @Override
    public <T> List<T> top(int top, Class<T> clazz, String hql, Object... args) {
        return query(clazz, hql, args).setMaxResults(top).getResultList();
    }

    // ---------------------------------exec
    protected int exec(String ql) {
        return entityManager.createQuery(ql).executeUpdate();
    }

    protected int exec(String ql, Object arg) {
        return entityManager.createQuery(ql).setParameter(1, arg).executeUpdate();
    }

    protected int exec(String ql, Object arg, Object arg2) {
        return entityManager.createQuery(ql).setParameter(1, arg).setParameter(2, arg2).executeUpdate();
    }

    protected int exec(String ql, Object arg, Object arg2, Object arg3) {
        return entityManager
                .createQuery(ql)
                .setParameter(1, arg)
                .setParameter(2, arg2)
                .setParameter(3, arg3)
                .executeUpdate();
    }
}
